﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VFTray.Mi
{
	internal class ProcessVerify
	{
		public ProcessVerify()
		{
			Count = 0;
		}
		private int S1 = 0;
		private int S2 = 0;
		internal DateTime DtCurrent=DateTime.Now.AddDays(30);
		internal DateTime DtExpired = DateTime.Now.AddDays(-1);

		internal string GetExpiredS()
		{
			TimeSpan ts = DtExpired - DateTime.Now;
			if (ts.TotalDays >= 0 && ts.TotalDays < 100)
				return string.Format("有效期剩余{0}天", (int)ts.TotalDays);
			else if (ts.TotalDays < 0)
				return string.Format("未验证");
			else
				return string.Format("已验证");
		}

		string fn_rc_pri_crypt(string path) { return Path.Combine(path, "crypri.dll"); }
		string fn_rs_pub_crypt(string path) { return Path.Combine(path, "sigpub.dll"); }

		string fn_sig_crypt(string path) { return Path.Combine(path, "rcpri.sig"); }
		string fn_rundata(string path) { return Path.Combine(path, "runperm.tov"); }

		internal int Verify(string path)
		{
			try
			{
				string guid = LoadGuid();
				if (string.IsNullOrEmpty(guid)) return 1;
				if (!File.Exists(fn_rc_pri_crypt(path))) return 2;
				if (!File.Exists(fn_rs_pub_crypt(path))) return 3;
				if (!File.Exists(fn_sig_crypt(path))) return 4;

				var by_rc_pri_crypt = FileHelper.ReadByte(fn_rc_pri_crypt(path));
				var by_rc_pri = DESHelper.Decrypt(by_rc_pri_crypt, guid);

				var by_rs_pub_crypt = FileHelper.ReadByte(fn_rs_pub_crypt(path));
				var by_rs_pub = Mi.DESHelper.Decrypt(by_rs_pub_crypt, guid);
				var str_rs_pub = Encoding.UTF8.GetString(by_rs_pub);

				var by_sig_crypt = FileHelper.ReadByte(fn_sig_crypt(path));
				var by_sig = Mi.DESHelper.Decrypt(by_sig_crypt, guid);

				if(RSAHelper.Verify(by_rc_pri, by_sig, str_rs_pub))
				{
					return 0;
				}
				return -2;
			}
			catch(Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("ProcessVerifyV" + ex.Message);
				return -1;
			}
		}
		internal int LoadData(string path)
		{
			string guid = LoadGuid();
			if (string.IsNullOrEmpty(guid)) return 1;

			if (!File.Exists(fn_sig_crypt(path))) return 2;
			if (!File.Exists(fn_rundata(path))) return 3;

			string sk = GetKeyIV(fn_sig_crypt(path), guid);
			if (string.IsNullOrEmpty(sk)) return 4;

			try
			{
				var by_content_crypt = FileHelper.ReadByte(fn_rundata(path));
				byte[] by_content = DESHelper.Decrypt(by_content_crypt, sk);
				string str_content = Encoding.UTF8.GetString(by_content);
				var parts = str_content.Split(new char[] { ';' });
				S1 = Convert.ToInt32(parts[0]);
				S2 = Convert.ToInt32(parts[1]);
				string sCd = parts[2];
				string sEd = parts[3];

				DtCurrent = DateTime.ParseExact(sCd, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
				DtExpired = DateTime.ParseExact(sEd, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
			}
			catch(Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("Read RUN DATA " + ex.Message);
				return -1;
			}


			return 0;
		}

		internal bool CheckCurrent()
		{
			TimeSpan ts = DtCurrent - DateTime.Now;
			if (ts.TotalDays > 30) return false;
			return true;
		}

		internal int UpdateData(string path, string dd)
		{
			DtExpired = DateTime.ParseExact(dd, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
			return UpdateData(path);
		}

		internal int UpdateData(string path)
		{
			try
			{
				string guid = LoadGuid();
				if (string.IsNullOrEmpty(guid)) return 1;

				if (!File.Exists(fn_sig_crypt(path))) return 2;
				if (!File.Exists(fn_rundata(path))) return 3;

				string sk = GetKeyIV(fn_sig_crypt(path), guid);
				if (string.IsNullOrEmpty(sk)) return 4;


				string str_content = S1.ToString() + ";" + S2.ToString() + ";" + DateTime.Now.ToString("yyyyMMdd") + ";" + DtExpired.ToString("yyyyMMdd");
				byte[] by_content = Encoding.UTF8.GetBytes(str_content);

				byte[] by_content_crypt = DESHelper.Encrypt(by_content, sk);
				Mi.FileHelper.WriteByte(by_content_crypt, fn_rundata(path));
				return 0;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("UpdateData " + ex.Message);
				return -1;
			}
		}

		internal string Guid { get { return guid; } }
		private string guid = "";

		internal ushort Count { get; set; }
		internal string LoadGuid(bool force = false)
		{
			if (string.IsNullOrEmpty(guid) || force)
			{
				AuthUtility auth = new AuthUtility();
				var ss = auth.GetContent();

				var ps = ss.Split(new char[] { ' ' });
				guid = ps[0];
				try
				{
					Count = Convert.ToUInt16(ps[1]);
				}
				catch (Exception ex)
				{
					Count = 0;
				}

				return guid;
			}
			return guid;
		}

		private string GetKeyIV(string fn_sig_crypt, string guid)
		{
			try
			{
				var by_sig_crypt = FileHelper.ReadByte(fn_sig_crypt);
				var by_sig = Mi.DESHelper.Decrypt(by_sig_crypt, guid);

				string sk = DesPwd(by_sig);
				return sk;
			}
			catch(Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("GetKeyIV " + ex.Message);
				return "";
			}
		}

		private string DesPwd(byte[] sig)
		{
			byte[] dk = new byte[8];
			byte[] iv = new byte[8];
			Array.Copy(sig, dk, 8);
			Array.Copy(sig, 8, iv, 0, 8);
			string sk = CodeHelper.ToBase32String(dk) + CodeHelper.ToBase32String(iv);
			return sk;
		}

		#region for main loop

		public EventHandler<Tuple<int,int,string>> ECheckU;
		internal void OnTick()
		{
#if !NOKEY
			updateVerifyU(false,-1);
			updateVerify(false);
#endif
		}


		internal void updateVerifyU(bool force, int count)
		{
			int checkInter = DateTime.Now.Hour % 2;
			if (force)
			{
				checkInter = 0;
			}

			if (checkInter == 0)
			{
				Mi.AuthUtility au = new Mi.AuthUtility();
				if (au.Load() == false)
				{
					if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(-1, 0, "CANNOT FIND UKEY"));
					return;
				}
				else
				{
					LoadGuid(true);
					if (count > 0)
					{
						if (count != Count)
						{
							if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(-2, 0, "TRAY NUMBER IS NOT CORRECT"));
						}
					}
				}
			}
		}

		private int lastHour = -1;
		internal void updateVerify(bool force)
		{
			if (force) lastHour = -1;
			DateTime dtnow = DateTime.Now;
			int hour = dtnow.Hour;
			if (hour != lastHour)
			{
				lastHour = hour;

				lock (this)
				{
					int rtn_v = Verify(Environment.CurrentDirectory);
					if (rtn_v == 0)
					{
						int rtn_l = LoadData(Environment.CurrentDirectory);
						if (rtn_l == 0)
						{
							if (DtExpired > dtnow)
							{
								if (CheckCurrent())
								{
									int rtn_u = UpdateData(Environment.CurrentDirectory);
									if (rtn_u == 0)
									{
										if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(0, 0, ""));
										//GlobalParams.PC.Status_Verified.Update(true);
									}
									else
									{
										if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(1, rtn_u, ""));
										//GlobalParams.PC.Status_Verified.Update(false);
										//GlobalParams.PC.processVerificationStop();
										//Text = OrgTitle + System.Diagnostics.FileVersionInfo.GetVersionInfo(Application.ExecutablePath).FileVersion + " 未验证";
										//MessageBox.Show("NO VARIFICATION UPDATE CTIME " + rtn_u.ToString());
									}
								}
								else
								{
									if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(2, 0, ""));

									//GlobalParams.PC.Status_Verified.Update(false);
									//GlobalParams.PC.processVerificationStop();
									//Text = OrgTitle + System.Diagnostics.FileVersionInfo.GetVersionInfo(Application.ExecutablePath).FileVersion + " 未验证";
									//MessageBox.Show("NO VARIFICATION CHECK CTIME");
								}
							}
							else
							{
								if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(3, 0, ""));

								//GlobalParams.PC.Status_Verified.Update(false);
								//GlobalParams.PC.processVerificationStop();
								//Text = OrgTitle + System.Diagnostics.FileVersionInfo.GetVersionInfo(Application.ExecutablePath).FileVersion + " 未验证";
								//MessageBox.Show("NO VARIFICATION ETIME");
							}
						}
						else
						{
							if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(4, rtn_l, ""));

							//GlobalParams.PC.Status_Verified.Update(false);
							//GlobalParams.PC.processVerificationStop();
							//Text = OrgTitle + System.Diagnostics.FileVersionInfo.GetVersionInfo(Application.ExecutablePath).FileVersion + " 未验证";
							//MessageBox.Show("NO VARIFICATION DATA" + rtn_l.ToString());
						}
					}
					else
					{
						if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(5, rtn_v, ""));
						//GlobalParams.PC.Status_Verified.Update(false);
						//GlobalParams.PC.processVerificationStop();
						//Text = OrgTitle + System.Diagnostics.FileVersionInfo.GetVersionInfo(Application.ExecutablePath).FileVersion + " 未验证";
						//MessageBox.Show("NO VALIDATION DATA " + rtn_v.ToString());
					}

					//each hour
					if (LLCVApi.CVAPI.CheckAuth() == 0)
					{//OK
					}
					else
					{//NG
						if (ECheckU != null) ECheckU(this, new Tuple<int, int, string>(-3, 0, "CV FAILED CHECK"));
					}
				}
			}
		}

		internal void ShowVerificationDlg()
		{
#if !NOKEY
			Mi.DlgVarification dlg = new Mi.DlgVarification(this);
			dlg.ShowDialog();
#endif
		}
		#endregion for main loop
	}
}
